use std::fmt;
use std::io::{Result};
use std::ops::{Deref, DerefMut, Drop};
use std::ffi::c_void;

use super::raw::Raw;

pub struct MBox<T> {
    data: std::ptr::NonNull<T>,
}

impl<T> MBox<T> {
    pub fn reset(&mut self,data: T) {
        unsafe {
            *(self.data.as_mut()) = data;
        }   
    }
    pub fn attach(h: &Raw, offset: usize) -> Result<Self> {
        let ptr = h.from_offset(offset).to_ptr::<T>()?;
        Ok(MBox{data: ptr})
    }
    pub fn as_raw(&self) -> Raw {
        Raw{ h : Some(self.data.as_ptr() as *const c_void) }
    }
    pub fn as_type<P>(&self) -> Result<MBox<P>> {
        let r = self.as_raw();
        MBox::<P>::attach(&r,0 as usize)
    }
    pub fn as_subtype<P>(&self,offset : usize) -> Result<MBox<P>> {
        let r = self.as_raw();
        MBox::<P>::attach(&r,offset)
    }
}

impl<T> fmt::Debug for MBox<T> where T: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self.data)
    }
}

impl<T> Drop for MBox<T> {
    fn drop(&mut self) {}
}

impl<T> Deref for MBox<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        unsafe { self.data.as_ref() }
    }
}

impl<T> DerefMut for MBox<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { self.data.as_mut() }
    }
}
